#property copyright "Copyright 2024"
#property link      ""
#property version   "1.00"
#property strict

// 输入参数
input int    BB_Period = 20;        // 布林带周期
input double BB_Deviation = 2.0;    // 布林带标准差倍数
input int    MinExpansionBars = 3;  // 最小开口持续K线数
input double MinWidthChange = 0.0001; // 最小宽度变化阈值

// 全局变量
int lastCalculatedBar = 0;          // 上次计算的K线索引
bool isExpanding = false;           // 当前是否处于开口状态
datetime expansionStartTime = 0;     // 开口开始时间
double lastWidth = 0;               // 上一次的带宽
int expansionCount = 0;             // 开口持续计数

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // 清理所有绘制的对象
    ObjectsDeleteAll(0, "BB_Expansion_");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 只在新的K线形成时计算
    if(lastCalculatedBar == iBarShift(_Symbol, PERIOD_CURRENT, iTime(_Symbol, PERIOD_CURRENT, 0)))
        return;
        
    lastCalculatedBar = iBarShift(_Symbol, PERIOD_CURRENT, iTime(_Symbol, PERIOD_CURRENT, 0));
    
    // 计算布林带
    double upperBand[], middleBand[], lowerBand[];
    ArraySetAsSeries(upperBand, true);
    ArraySetAsSeries(middleBand, true);
    ArraySetAsSeries(lowerBand, true);
    
    int calculated = iBands(_Symbol, PERIOD_CURRENT, BB_Period, 0, BB_Deviation, PRICE_CLOSE, MODE_UPPER, 0, upperBand);
    if(calculated <= 0) return;
    
    calculated = iBands(_Symbol, PERIOD_CURRENT, BB_Period, 0, BB_Deviation, PRICE_CLOSE, MODE_MAIN, 0, middleBand);
    if(calculated <= 0) return;
    
    calculated = iBands(_Symbol, PERIOD_CURRENT, BB_Period, 0, BB_Deviation, PRICE_CLOSE, MODE_LOWER, 0, lowerBand);
    if(calculated <= 0) return;
    
    // 计算当前带宽
    double currentWidth = upperBand[0] - lowerBand[0];
    double widthChange = currentWidth - lastWidth;
    
    // 判断是否处于开口状态
    bool wasExpanding = isExpanding;
    isExpanding = MathAbs(widthChange) > MinWidthChange;
    
    // 更新开口状态
    if(isExpanding)
    {
        if(!wasExpanding)
        {
            // 开始新的开口状态
            expansionStartTime = iTime(_Symbol, PERIOD_CURRENT, 1);
            expansionCount = 1;
        }
        else
        {
            expansionCount++;
        }
    }
    else
    {
        if(wasExpanding && expansionCount >= MinExpansionBars)
        {
            // 开口状态结束，绘制区域
            DrawExpansionZone(expansionStartTime, iTime(_Symbol, PERIOD_CURRENT, 1));
        }
        expansionCount = 0;
    }
    
    lastWidth = currentWidth;
}

//+------------------------------------------------------------------+
//| 绘制开口区域                                                      |
//+------------------------------------------------------------------+
void DrawExpansionZone(datetime startTime, datetime endTime)
{
    string objName = "BB_Expansion_" + TimeToString(startTime);
    
    if(!ObjectCreate(0, objName, OBJ_RECTANGLE, 0, startTime, 0, endTime, 0))
    {
        Print("创建开口区域对象失败: ", GetLastError());
        return;
    }
    
    // 设置对象属性
    ObjectSetInteger(0, objName, OBJPROP_COLOR, C'128,128,128');
    ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_SOLID);
    ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
    ObjectSetInteger(0, objName, OBJPROP_FILL, true);
    ObjectSetInteger(0, objName, OBJPROP_BACK, true);
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
    ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
    ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0);
    
    // 设置价格范围
    double high = iHigh(_Symbol, PERIOD_CURRENT, iBarShift(_Symbol, PERIOD_CURRENT, startTime));
    double low = iLow(_Symbol, PERIOD_CURRENT, iBarShift(_Symbol, PERIOD_CURRENT, endTime));
    
    ObjectSetDouble(0, objName, OBJPROP_PRICE1, high);
    ObjectSetDouble(0, objName, OBJPROP_PRICE2, low);
} 